#pragma once

#include <windows.h>
#include <memory>
#include <vector>
#include "esgSet.h"


namespace cudaEsg
{

	using namespace std;


	enum ComputeRatingsOptions
	{
		ComputeIndicatorsRatingsNone = 0,
		ComputeIndicatorsRatingsOption = 1,
		ComputeRankingsOption = 2,
		ComputeReverseCompanyIndexOption = 4,
		ComputeAggregatedWeights=8,
		ReturnWeights=16,
		DefaultWeightMatrix = 32
	};

	DEFINE_ENUM_FLAG_OPERATORS(ComputeRatingsOptions);

	typedef enum  {
		esgNotImplemented = -2,
		esgError = -1,
		esgSuccess = 0,
		esgDuplicateEntity = 1,
		esgSetNotFound = 2,
		esgUniverseNotFound = 3,
		esgMemoryAllocationError = 4,
		esgInvalidParameters = 5,
		esgMatrixNotFound = 6,
		esgAttributesNotFound = 7,
		esgParametersMismatch = 8,



		esgCompaniesCountMismatch = 9,
		esgIndicatorsCountMismatch = 10,
		esgClusterGroupsMismatch = 11,

		esgMaxDisjunctClustersCount = 12,
		esgMaxDisjunctClustersHeads = 13,
		esgInvalidEsgEndsCount = 14,

		esgMatrixInfoNotFound = 15,
		esgInvalidSetId=16,



		esgIndicatorsScoresNotFound = 50,
		governanceWeightIssuesNotFound = 60,
		governanceCombinationModifiersNotFound = 61,



		esgCudaInvalidArch = 90,
		esgCudaError = 100,
	} cudaEsg_errors_t;

	struct operation_errors_t {
		cudaEsg_errors_t esgError;
		int internalError;

		const char* errorString = nullptr;
		const char* internalErrorString = nullptr;
		const char* file = nullptr;
		const char* functionName = nullptr;
		int lineNo;

		static operation_errors_t success() {
			operation_errors_t ret;
			ret.esgError = esgSuccess;
			ret.internalError = 0;
			return ret;
		}

		operation_errors_t() {
			esgError = esgSuccess;
			internalError = 0;
		}

		operation_errors_t(cudaEsg_errors_t esgError, int internalError) {
			this->esgError = esgError;
			this->internalError = internalError;
		}

		operation_errors_t(cudaEsg_errors_t esgError, int internalError, const char* internalErrorString, const char* functionName, int lineNo, const char* file) {
			this->esgError = esgError;
			this->internalError = internalError;
			this->internalErrorString = internalErrorString;
			this->functionName = functionName;
			this->file = file;
			this->lineNo = lineNo;
		}

		operation_errors_t(cudaEsg_errors_t esgError) {
			this->esgError = esgError;
			this->internalError = 0;
		}

		operation_errors_t(cudaEsg_errors_t esgError, const char* errorString) {
			this->esgError = esgError;
			this->errorString = errorString;
		}
	};

	struct SetConfiguration {
		int setId=-1;
		int companiesCount;
		

		int esgHeadsCount;
		byte * esgHeads;

		///clusters intervals(without esg), should be at most MAX_CLUSTERS_HEADS values
		int disjunctClustersHeadsCount;
		byte* disjunctClustersHeads;

		///clusters groups(without esg) should contain values from 0 to disjunctClustersCount-1
		int disjunctClustersMapCount;
		byte* disjunctClustersMap;

		//all clusteres including ALL, ESG, DISJUNCT CLUSTERS
		int CLUSTERS_COUNT = 8;
		int indicatorsCount = -1;

		//clusters no (witouth all and esg)
		int DISJUNCT_CLUSTERS_COUNT = 4;
	};

	class Operation
	{
	public:
		std::string name;
		double elapsedTimeMilliseconds;

		Operation(): elapsedTimeMilliseconds(0)
		{
		}

		Operation(std::string name, double elaspsedSeconds){
			this->name = name;
			this->elapsedTimeMilliseconds = elaspsedSeconds * 1000;
		}

		~Operation(){
		}
	};

	class MemoryOperation
	{
	public:
		std::string name;
		double bytes;

		MemoryOperation(std::string name, double bytes){
			this->name = name;
			this->bytes = bytes;
		}

		~MemoryOperation(){
		}
	};

	class RatingsResult {
	public:
		cudaEsg::ComputeRatingsOptions options = cudaEsg::ComputeIndicatorsRatingsNone;
		int companiesCount;
		int indicatorsCount;
		int clustersCount;

		//cluserNo * companiesCount
		int *indices;
		int *reverese_indices;

		//cluserNo * companiesCount
		float *ratings;

		//indicatorsNo * companiesCount
		float *indicators_ratings;

		int clusterGroupsCount;

		int *rankings;
		float *group_mins;
		float *group_maxs;
		float *group_avgs;
		int *group_counts;
		float *percentile;
		float *aggregatedWeights;
		float *weights;
		bool *isInReferenceUniverse;

		std::unique_ptr<std::vector<std::unique_ptr<Operation>>> timedOperations= std::make_unique<std::vector<std::unique_ptr<Operation>>>();


		RatingsResult() {	
		}

		RatingsResult(RatingsResult &&){}

		RatingsResult(const RatingsResult&) = default;

		RatingsResult& operator=(const RatingsResult&) = default;

		~RatingsResult() {

		}
	};

	class GovernanceRatingsResult {
	public:
		int companiesCount=0;
		int clustersCount=0;
		int indicatorsCount=0;
		int groupingCategoriesCount=0;

		//cluserNo * companiesCount
		float *ratings;
		int* indices;

		//indicatorsNo * companiesCount

		int clusterGroupsCount=0;

		int *rankings;
	
		int *group_counts;
		float *percentile;

		float* weights;
		float* issue_Weighted_Scores;


		GovernanceRatingsResult(int companies_count, int clusters_count, int indicators_count, int grouping_categories_count, int cluster_groups_count)
			: companiesCount(companies_count),
			  clustersCount(clusters_count),
			  indicatorsCount(indicators_count),
			  groupingCategoriesCount(grouping_categories_count),
			  clusterGroupsCount(cluster_groups_count)
		{
		}

		GovernanceRatingsResult() {

		}

		~GovernanceRatingsResult() {

		}
	};

	class MemoryOperations
	{
	public:
		std::unique_ptr<std::vector<std::unique_ptr<MemoryOperation>>> operationsList;

		MemoryOperations() {
			operationsList = std::make_unique<std::vector<std::unique_ptr<MemoryOperation>>>();
		}

		~MemoryOperations() {
			operationsList.reset();
		}
	};

	class ComputeContext
	{
	public:
		virtual ~ComputeContext() {};
		MemoryOperations* memoryStatistics;
		virtual operation_errors_t Ratings(int setId, int universeId, int weightMatrixId, unsigned char filter, int groupsHeadsCount, int* groupHeads, RatingsResult &result) = 0;
		virtual operation_errors_t GovernanceRatings(int setId, int weightMatrixId, bool includeWeights, bool includeWeightedIssues,  GovernanceRatingsResult &result) = 0;
		virtual operation_errors_t AddUniverse(int setId, int universeId, int companiesCount, unsigned char * universe) = 0;
		virtual operation_errors_t DeleteUniverse(int setId, int universeId) = 0;
		virtual operation_errors_t SetAttributes(int setId, int companiesCount, unsigned char * attributes) = 0;
		virtual operation_errors_t SetCompaniesScores(int setId, int scoresCount, float *scoresScores) = 0;
		virtual operation_errors_t SetCompaniesWeights(int setId, int scoresCount, float *scoresScores) = 0;
		virtual operation_errors_t SetCompanyGroupFlags(int setId, int companiesCount, int attributes_Types_Count, int regionGroupsCount, int peerGroupsCount, int* attributes, int attributesLenght) = 0;
		//virtual operation_errors_t SetCompanyGroupFlags(int setId, int companiesCount, int attributes_Types_Count, vector<ComparativeGroup> attributes) = 0;
		virtual operation_errors_t AddMatrix(int setId, int matrixId, int weightsCount, float * weightsMatrix,int profilesCount,unsigned char* profileFlags ) = 0;
		virtual operation_errors_t DeleteMatrix(int setId, int matrixId) = 0;
		virtual operation_errors_t AddSet(SetConfiguration* configuration) = 0;
		virtual operation_errors_t SetIndicators(int setId, int companiesCount, int indicatorsCount, float *indicatorScores) = 0;
		virtual operation_errors_t DeleteSet(int setId) = 0;
		virtual operation_errors_t CleanContext(int* &ids, int &count) = 0;
		virtual operation_errors_t FillSetAttributes(int setId, int &companiesCount, int &indicatorsCount, int &clusterGroupsCount, int &clustersCount) = 0;
		virtual operation_errors_t FillSetAttributes(int setId, int &companiesCount, int &indicatorsCount, int &clusterGroupsCount, int &clustersCount, int& attributes_Types_Count ) = 0;
		virtual operation_errors_t SetCombinationModifiers(int setId, int matrixId, int weightsCount, float* weights)=0;
		virtual operation_errors_t SetIssueWeights(int setId, int matrixId, int weightsCount, float* weights) = 0;

	};

	class ComputeContextException : public std::exception {
	public:
		operation_errors_t operation_error;

		ComputeContextException(operation_errors_t opErrr) throw() {
			operation_error = opErrr;
		}

		virtual const char* what() const throw() {
			return "cuda operation error";
		}
	};

	class ComputeContextFactory
	{
		public:
			std::unique_ptr<ComputeContext> CreateContext() const;
	};

}
